package com.dly.blog.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.dly.blog.content.RedisContent;
import com.dly.blog.domain.dto.FlyResDto;
import com.dly.blog.domain.dto.GptResponseDto;
import com.dly.blog.domain.dto.MessageContentDto;
import com.dly.blog.domain.enums.ChatType;
import com.dly.blog.domain.ro.ChatGptRO;
import com.dly.blog.exception.ChatExceptionEnum;
import com.dly.blog.provider.user.UserProvider;
import com.dly.blog.service.ChatProxy;
import com.dly.blog.service.ChatService;
import com.dly.blog.utile.JsonUtils;
import com.dly.blog.utile.RedisUtils;
import com.dly.blog.utile.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@AllArgsConstructor
@Slf4j
public class ChatServiceImpl implements ChatService {

    private final RedisUtils redisUtils;

    private final UserProvider userProvider;

    private final List<ChatProxy> chatProxies;

    @Override
    public String saveMessages(ChatGptRO chatGptRO) {
        //次数不够
        if (!userProvider.chickFrequency(1)) {
            throw ChatExceptionEnum.FREQUENCY_NOT_ENOUGH.getChatException();
        }
        //发送内容为空
        if (chatGptRO.getMessages()
                .stream()
                .anyMatch(contentDto -> StrUtil.isBlank(contentDto.getContent()))) {
            throw ChatExceptionEnum.MESSAGE_IS_EMPTY.getChatException();
        }
        //减少一次
        userProvider.reducedFrequency(SecurityUtils.getUserId().toString(), 1);
        String uuid = UUID.fastUUID().toString(true);
        redisUtils.setCacheObject(RedisContent.USER_MESSAGES + uuid + ":" + SecurityUtils.getUserId(), chatGptRO, 2, TimeUnit.MINUTES);
        return uuid + ":" + SecurityUtils.getUserId();
    }

    public ChatProxy getChatProxy(ChatType chatType) {
        return chatProxies.stream()
                .filter(chatProxy -> chatProxy.matching(chatType))
                .findAny()
                .orElseThrow(ChatExceptionEnum.CHAT_GET_CLIENT_ERROR::getChatException);
    }

    @Override
    public SseEmitter gptMvc(String uuid) {
        //0L 关闭sse的超时时间
        SseEmitter emitter = new SseEmitter(0L);
        ChatGptRO chatGptRO = redisUtils.getCacheObject(RedisContent.USER_MESSAGES + uuid);
        if (Objects.isNull(chatGptRO)) {
            try {
                emitter.send(SseEmitter.event().name("DONE").data("消息接受异常请重新发送"));
                return emitter;
            } catch (IOException e) {
                throw ChatExceptionEnum.SSE_SEND_ERROR.getChatException();
            }
        }
        ChatProxy chatProxy = getChatProxy(chatGptRO.getChatType());
        chatProxy.send(chatGptRO.getMessages())
                .map(content ->
                        switch (chatGptRO.getChatType()) {
                            case FOUR_CHAT,THREE_CHAT -> {
                                if (content.equals("[DONE]")) {
                                    yield SseEmitter.event().name("DONE").data(-1);
                                }
                                GptResponseDto.Choices choices = JsonUtils.readFromJsonString(content, GptResponseDto.class).getChoices().get(0);
                                //由于单独传输容易丢失空格
                                yield SseEmitter.event().name("chat")
                                        .data(Objects.requireNonNull(JsonUtils.writeToJson(choices)), MediaType.TEXT_PLAIN);
                            }
                            case FLY -> {
                                FlyResDto flyResDto = JsonUtils.readFromJsonString(content, FlyResDto.class);
                                if (flyResDto.getHeader().getStatus().equals(2)) {
                                    yield SseEmitter.event().name("DONE").data(-1);
                                }
                                GptResponseDto.Choices cover = cover(flyResDto.getPayload().getChoices());
                                yield SseEmitter.event().name("chat")
                                        .data(Objects.requireNonNull(JsonUtils.writeToJson(cover)), MediaType.TEXT_PLAIN);
                            }
                        })
                .subscribe(objectServerSentEvent -> {
                    try {
                        emitter.send(objectServerSentEvent);
                    } catch (IOException e) {
                        log.error("错误数据：{}", objectServerSentEvent);
                        throw ChatExceptionEnum.SSE_SEND_ERROR.getChatException();
                    }
                }, error -> {
                    if (!(error instanceof ChatExceptionEnum.ChatException)) {
                        userProvider.addFrequency(uuid.split(":")[1], 1);
                    }
                    log.error("获取消息异常：{}", error.getMessage());
                    try {
                        emitter.send(SseEmitter.event().name("DONE").data("服务器连接异常"));
                    } catch (IOException e) {
                        throw ChatExceptionEnum.SSE_SEND_ERROR.getChatException();
                    }
                }, emitter::complete);
        return emitter;
    }

    public GptResponseDto.Choices cover(FlyResDto.Choices choices) {
        GptResponseDto.Choices choices1 = new GptResponseDto.Choices();
        MessageContentDto messageContentDto = new MessageContentDto();
        messageContentDto.setRole(choices.getText().get(0).getRole());
        messageContentDto.setContent(choices.getText().get(0).getContent());
        choices1.setDelta(messageContentDto);
        return choices1;
    }

    @Override
    public Flux<ServerSentEvent<Object>> gpt() {
        ChatGptRO chatGptRO = redisUtils.getCacheObject(RedisContent.USER_MESSAGES + SecurityUtils.getUserId());
        ChatProxy chatProxy = getChatProxy(chatGptRO.getChatType());
        return chatProxy.send(chatGptRO.getMessages())
                .map(content -> "[DONE]".equals(content) ?
                        //消息结束时 推送DONE事件通知关闭
                        ServerSentEvent.builder().event("DONE").data(-1).build() :
                        ServerSentEvent.builder().event("chat")
                                .data(JSONUtil.toBean(content, GptResponseDto.class).getChoices().get(0).getDelta().getContent())
                                .build()
                ).timeout(Duration.ofSeconds(60L));
    }


    public Boolean showWxMiniApp() {
        return redisUtils.getCacheObject("showWxMiniApp");
    }

    public void setShowWxMiniApp(Boolean b) {
        redisUtils.setCacheObject("showWxMiniApp", b);
    }
}
